package com.syf.kit.core.dao.mapper.registry;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.annotation.TableName;
import com.syf.core.utils.classscan.AnnotationClassFilter;
import com.syf.core.utils.classscan.MixedClassFilter;
import com.syf.core.utils.classscan.ScanClassFromClassPath;
import com.syf.core.utils.classscan.TypeClassFilter;
import com.syf.kit.core.dao.BaseDTO;
import com.syf.kit.core.dao.mapper.SyfMapper;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import org.apache.ibatis.session.SqlSessionFactory;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * sy frame dto mapper注册器
 * T为ORM框架mapper注册受众对象
 */
public abstract class SyfAbstractDtoMapperRegistry<T> {
    private final Map<String, Class<SyfMapper>> mapperMap = new HashMap<>();

    /**
     * @param scanPkg 扫描dto类的初始package
     */
    public SyfAbstractDtoMapperRegistry(String scanPkg) {
        scanDTO(scanPkg);
    }

    /**
     * 扫描mapperPkg下既有@Table注解，同时又是BaseDTO子类的类
     *
     * @param scanPkg 扫描dto类的初始package
     */
    public void scanDTO(String scanPkg) {
        if (StrUtil.isEmpty(scanPkg)) {
            throw new NullPointerException("scanPkg is empty String");
        }

        // 1.扫描scanPkg下所有带@Table注解的BaseDTO子类
        MixedClassFilter dtoClassFilter = new MixedClassFilter(new TypeClassFilter(BaseDTO.class), new AnnotationClassFilter(TableName.class));
        ScanClassFromClassPath scanHandler = new ScanClassFromClassPath(dtoClassFilter);
        Set<Class<?>> dtoClassSet = scanHandler.getPackageAllClasses(scanPkg, true);

        // 2、生成对应mapper接口的字节码，并加载至jvm
        loadMapperByDTO(dtoClassSet);
    }

    /**
     * 通过byte buddy技术，根据DTO创建对应的mapper字节码，并加载至jvm
     */
    private void loadMapperByDTO(Set<Class<?>> dtoClassSet) {
        for (Class<?> dtoClazz : dtoClassSet) {
            // 1.通过byte buddy创建mapper接口，并加载值jvm
            Class<SyfMapper> mapperClazz = (Class<SyfMapper>) new ByteBuddy()
                    .makeInterface(TypeDescription.Generic.Builder.parameterizedType(SyfMapper.class, dtoClazz).build())
                    .name(dtoClazz.getTypeName().concat("Mapper"))
                    .make()
                    .load(SyfAbstractDtoMapperRegistry.class.getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
                    .getLoaded();

            // 2.map 保存mapper class
            mapperMap.put(dtoClazz.getTypeName(), mapperClazz);
        }
    }

    protected Map<String, Class<SyfMapper>> getAllMapperMap() {
        return mapperMap;
    }

    /**
     * 根据dto类获取对应mapper对象
     *
     * @param dtoClass dto类
     */
    public <D extends BaseDTO> Class<SyfMapper> getMapperType(Class<D> dtoClass) {
        return mapperMap.get(dtoClass.getTypeName());
    }

    /**
     * 设置基础Mapper的Class
     */
    public abstract Class getBaseMapperType();

    /**
     * 将自定义的mapper，并加载使用.本类对象使用前，必须完成此方法调用
     *
     * @param sqlSessionFactory mybatis sqlSessionFactory
     * @param t                 为ORM框架mapper注册受众对象
     */
    public abstract void registerMapper(SqlSessionFactory sqlSessionFactory, T t);
}
